package middleware

import (
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"go-gin-api/core/config"
	re "go-gin-api/core/redis"
	"go-gin-api/exception"
	"net/http"
	"time"
)

var JwtKey = []byte(config.GetString("app.jwt.key")) // 盐

type MyClaims struct {
	LoginName string `gorm:"json:"login_name"` // 要和User模块的字段一样
	//Password           string `gorm:"json:"password"`
	jwt.StandardClaims // 嵌套 dgrijalva/jwt-go 自己带的结构体，有签发时间、签发人...等字段
}

// SetToken 生成 token
func SetToken(loginName string) (token string, err error) {
	expire, _ := time.ParseDuration(config.GetString("app.write.time"))
	expireTime := time.Now().Add(expire) // 过期时间
	SetClaims := MyClaims{
		LoginName: loginName, // todo 这里 userId比较合理
		// todo 应该加上权限码
		//Password:  Password,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expireTime.Unix(),
			Issuer:    "super",
		},
	}

	reqClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, SetClaims) // 生成token。SigningMethodHS256 是之中的一种模式，哈希
	token, err = reqClaims.SignedString(JwtKey)
	if err != nil {
		return
	}
	re.Redis().SetEx(fmt.Sprintf(exception.UserToken, loginName), token, int64(expire/1000000000))
	return
}

// CheckToken 验证 token
func CheckToken(token string) (*MyClaims, error) {
	setToken, _ := jwt.ParseWithClaims(token, &MyClaims{}, func(token *jwt.Token) (interface{}, error) { // ParseWithClaims 比对token
		return JwtKey, nil
	})
	if key, _ := setToken.Claims.(*MyClaims); setToken.Valid {
		return key, nil
	} else {
		return nil, exception.TokenTimeout
	}
}

// JwtMiddleware jwt中间件，控制验证的东西。
func JwtMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 认证的东西
		tokenHeader := c.Request.Header.Get("Authorization") // 一种固定的规范

		// token 在不在
		if tokenHeader == "" {
			c.JSON(http.StatusOK, gin.H{
				"code":    exception.TokenNotExist.Code,
				"message": exception.TokenNotExist.Message,
			})
			c.Abort()
			return
		}

		// token格式检查。这是固定写法
		/*checkToken := strings.SplitN(tokenHeader, " ", 2)
		if len(checkToken) != 2 && checkToken[0] != "Bearer" {
			c.JSON(http.StatusOK, gin.H{
				"code":    exception.TokenTypeWrong.ErrCode(),
				"message": exception.TokenTypeWrong.Error(),
			})
			c.Abort()
			return
		}*/
		key, checkErr := CheckToken(tokenHeader)
		if checkErr != nil {
			check := checkErr.(*exception.Error)
			c.JSON(http.StatusOK, gin.H{
				"code":    check.ErrCode(),
				"message": check.Error(),
			})
			c.Abort()
			return
		}

		// 查过期
		if time.Now().Unix() > key.ExpiresAt {
			c.JSON(http.StatusOK, gin.H{
				"code":    exception.TokenTimeout.ErrCode(),
				"message": exception.TokenTimeout.Error(),
			})
			c.Abort()
			return
		}

		// 查完了。用户信息写入上下文
		c.Set("loginName", key.LoginName) // todo 反user全套信息
		c.Next()
	}
}
